package com.clhbolg.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.clhbolg.constant.RabbitMQConstant;
import com.clhbolg.constant.RedisConstant;
import com.clhbolg.domain.UserAuth;
import com.clhbolg.domain.UserInfo;
import com.clhbolg.domain.bo.UserAuthBo;
import com.clhbolg.enums.UserAreaTypeEnum;
import com.clhbolg.mapper.UserInfoMapper;
import com.clhbolg.model.dto.*;
import com.clhbolg.model.vo.request.ConditionVO;
import com.clhbolg.model.vo.request.UserInfoVO;
import com.clhbolg.model.vo.request.UserLoginVo;
import com.clhbolg.model.vo.request.UserVO;
import com.clhbolg.service.IUserAuthService;
import com.clhbolg.service.IUserInfoService;
import com.clhbolg.util.IpUtils;
import com.clhbolg.util.UserBuilderUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.exception.user.UserException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.SysLoginService;
import com.ruoyi.system.service.SysRegisterService;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 【请填写功能名称】Service业务层处理
 *
 * @author cailinhu
 * @date 2023-02-08
 */
@RequiredArgsConstructor
@Service
public class UserAuthServiceImpl implements IUserAuthService {


    @Autowired
    private ISysUserService iSysUserService;

    @Autowired
    SysRegisterService sysRegisterService;

    @Autowired
    SysLoginService sysLoginService;

    @Resource
    RabbitTemplate rabbitTemplate;

//    @Resource
//    UserInfoMapper userInfoMapper;

    @Autowired
    IUserInfoService userInfoService;

    @Resource
    UserInfoMapper userInfoMapper;

    @Resource
    RedisTemplate redisTemplate;




    private LambdaQueryWrapper<UserAuth> buildQueryWrapper(UserAuthBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<UserAuth> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserInfoId() != null, UserAuth::getUserInfoId, bo.getUserInfoId());
        lqw.like(StringUtils.isNotBlank(bo.getUsername()), UserAuth::getUsername, bo.getUsername());
        lqw.eq(StringUtils.isNotBlank(bo.getPassword()), UserAuth::getPassword, bo.getPassword());
        lqw.eq(bo.getLoginType() != null, UserAuth::getLoginType, bo.getLoginType());
        lqw.eq(StringUtils.isNotBlank(bo.getIpAddress()), UserAuth::getIpAddress, bo.getIpAddress());
        lqw.eq(StringUtils.isNotBlank(bo.getIpSource()), UserAuth::getIpSource, bo.getIpSource());
        lqw.eq(bo.getLastLoginTime() != null, UserAuth::getLastLoginTime, bo.getLastLoginTime());
        return lqw;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(UserAuth entity){
        //TODO 做一些数据校验,如唯一约束
    }


    @Override
    public UserDetailsDTO loginBlogUser(UserLoginVo vo) {
        SysUser sysUser = sysLoginService.loginBlogUser(vo.getUsername(), vo.getPassword());
        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
        UserInfo userInfo = userInfoMapper.selectById(sysUser.getUserId());
        UserDetailsDTO build = UserDetailsDTO.builder().id(sysUser.getUserId()).avatar(sysUser.getAvatar()).browser(userAgent.getBrowser().getName()).token(sysUser.getToken()).userInfoId(sysUser.getUserId())
            .ipSource(IpUtils.getIpSource(sysUser.getLoginIp())).nickname(sysUser.getNickName()).email(sysUser.getEmail()).loginType(1).intro(sysUser.getRemark()).isSubscribe(userInfo.getIsSubscribe())
            .roles(sysUser.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.toList())).username(sysUser.getUserName()).password(sysUser.getPassword()).website(userInfo.getWebsite())
            .isDisable(Integer.valueOf(sysUser.getStatus())).lastLoginTime(LocalDateTimeUtil.of(sysUser.getLoginDate())).ipAddress(sysUser.getLoginIp()).build();
        return build;
    }

    @Override
    public TableDataInfo<UserAdminDTO> listUsers(ConditionVO vo, PageQuery pageQuery) {
        SysUser sysUser = builderSysUser(vo);
        List<SysUser> sysUsers = iSysUserService.selectUserList(sysUser);
        List<UserAdminDTO> userAdminDTOS=builderUserDTO(sysUsers);
        return TableDataInfo.build(userAdminDTOS)
            ;
    }

    @Override
    @Transactional
    public void register(UserVO vo) {
        String o = Convert.toStr(redisTemplate.opsForValue().get(RedisConstant.USER_CODE_KEY + vo.getUsername()), "0");
        if (Objects.nonNull(o)&&o.equals(vo.getCode())){
            SysUser sysUser = new SysUser();
            sysUser.setUserName(vo.getUsername());
            sysUser.setPassword(BCrypt.hashpw(vo.getPassword()));
            sysUser.setEmail(vo.getUsername());
            sysUser.setUserType(UserType.EMAIL.getUserType());
            sysUser.setNickName("用户"+IdUtil.getSnowflake(1, 20).nextId());
            if (UserConstants.NOT_UNIQUE.equals(iSysUserService.checkUserNameUnique(sysUser))&&iSysUserService.checkEmailUnique(vo.getUsername())) {
                throw new UserException("user.register.save.error", vo.getUsername());
            }
            boolean regFlag = iSysUserService.registerUser(sysUser);
            if (!regFlag) {
                throw new UserException("user.register.error");
            }
            //记录什么几把日志
            sysRegisterService.recordLogininfor(vo.getUsername(), Constants.REGISTER, MessageUtils.message("user.register.success"));
            userInfoService.insertUserInfo(sysUser);
        }else {
            throw new GlobalException("验证码已经失效,请重新获取!");
        }
    }



    @Override
    public void sendCode(String username) {
        if (!ReUtil.isMatch(PatternPool.EMAIL,username)) {
           throw new GlobalException("邮箱无效");
        }
        //校验邮箱是否已经被注册
        if (iSysUserService.checkEmailUnique(username)) {
            throw new GlobalException("邮箱已经被注册");
        }
        int code = RandomUtil.randomInt(1000, 9999);
        Map<String, Object> map = new HashMap<>();
        map.put("content", "您的验证码为 " + code + " 有效期15分钟，请不要告诉他人哦！");
        EmailDTO emailDTO = EmailDTO.builder()
            .email(username)
            .subject("验证码")
            .commentMap(map).template("common.html")
            .build();
        rabbitTemplate.convertAndSend(RabbitMQConstant.EMAIL_EXCHANGE,"*",emailDTO);
        redisTemplate.opsForValue().set(RedisConstant.USER_CODE_KEY+username,code,15, TimeUnit.MINUTES);
    }

    /**
     * 修改用户信息， 多数据原的方式
     * @param userInfoVO
     */
    @Override
    @Transactional
    public void updateUserInfo(UserInfoVO userInfoVO) {
        //修改ruoyi的用户表
        Long userId = LoginHelper.getUserId();
        SysUser sysUser = UserBuilderUtil.builderSysUser(userInfoVO);
        sysUser.setUserId(userId);
        iSysUserService.updateUser(sysUser);
        userInfoService.updateUserInfo(userInfoVO,userId);
    }

    @Override
    public List<UserAreaDTO> listUserAreas(ConditionVO conditionVO) {
        List<UserAreaDTO> userAreaDTOs = new ArrayList<>();
        switch (Objects.requireNonNull(UserAreaTypeEnum.getUserAreaType(conditionVO.getType()))){
            case USER:
            case VISITOR:
                Map<String, Object> visitorArea = redisTemplate.opsForHash().entries(RedisConstant.VISITOR_AREA);
                if (Objects.nonNull(visitorArea)) {
                    userAreaDTOs = visitorArea.entrySet().stream()
                        .map(item -> UserAreaDTO.builder()
                            .name(item.getKey())
                            .value(Long.valueOf(item.getValue().toString()))
                            .build())
                        .collect(Collectors.toList());
                }
                return userAreaDTOs;
            default:
                break;
        }
        return userAreaDTOs;
    }

    private List<UserAdminDTO> builderUserDTO(List<SysUser> sysUsers) {
        List<UserAdminDTO> list = new ArrayList<>();
        for (SysUser sysUser : sysUsers) {
            UserAdminDTO user = UserAdminDTO.builder().id(sysUser.getUserId()).avatar(sysUser.getAvatar())
                .createTime(sysUser.getCreateTime()).ipSource(IpUtils.getIpSource(sysUser.getLoginIp())).nickname(sysUser.getNickName()).status(Integer.valueOf(sysUser.getStatus()))
                .isDisable(Integer.valueOf(sysUser.getStatus())).lastLoginTime(sysUser.getLoginDate()).ipAddress(sysUser.getLoginIp())
                //TODO s登录方式需要在登录的时候记录
                .loginType(1)
                .roles(builderRoles(sysUser.getRoles())).build();
            list.add(user);
        }
        return list;
    }

    private List<UserRoleDTO> builderRoles(List<SysRole> sysRoles){
       return sysRoles.stream().map(r->{
            UserRoleDTO build = UserRoleDTO.builder().roleName(r.getRoleName()).id(r.getRoleId()).build();
            return build;
        }).collect(Collectors.toList());
    }


    private SysUser builderSysUser(ConditionVO vo){
        SysUser sysUser = new SysUser();
        String keywords = vo.getKeywords();
        sysUser.setNickName(keywords);
        //TODO 需要设置登录类型
        sysUser.setUserType(Convert.toStr(vo.getType(),null));
        return sysUser;
    }
}
